﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MiniMax
{
    class MiniMax
    {
        internal static int dlimit = 3;
        internal static Random rnd = new Random();

        static void Main(string[] args)
        {
            int[,] board = {
							 {0, 2, 0, 2, 0, 2, 0, 2},
							 {2, 0, 2, 0, 2, 0, 2, 0},
							 {0, 0, 0, 2, 0, 2, 0, 0},
							 {2, 0, 0, 0, 0, 0, 2, 0},
							 {0, 0, 0, 1, 0, 0, 0, 1},
							 {1, 0, 0, 0, 1, 0, 0, 0},
							 {0, 1, 0, 1, 0, 1, 0, 1},
							 {1, 0, 1, 0, 1, 0, 1, 0}
							 };
            AIMove root = new AIMove(board);

            ArrayList moves = generateRedMoves(root.DeepClone(root));
            Console.WriteLine("Generated Red Moves");
            for (int i = 0; i < moves.Count; i++)
            {
                ((AIMove)moves[i]).printboard();
            }

            int rndpick = rnd.Next(0, moves.Count);
            AIMove pick = (AIMove)moves[rndpick];
            Console.WriteLine("Randomly Picked Next AIMove");
            pick.printboard();

            AIMove next = findMax(root, 0);
            
            if (next != null)
            {
                Console.WriteLine("Next MiniMax AIMove");
                next.printboard();
            }
        }

        internal static AIMove findMax(AIMove root, int depth)
        {
            AIMove next = null;

            if (depth == dlimit)
            {
                return root;
            }
            else
            {
                AIMove best = null;
                ArrayList moves = new ArrayList();
                moves = generateRedMoves(root);

                for (int i = 0; i < moves.Count; i++)
                {
                    
                    AIMove cand = findMin((AIMove)moves[i], depth + 1);

                    if (cand == null)
                    {
                    }
                    else if (best == null)
                    {
                        best = cand;
                    }
                    else if (cand.getPHV() > best.getPHV())
                    {
                        best = cand;
                    }
                    else if (cand.getPHV() == best.getPHV())
                    {
                        if (cand.getSRHV() > best.getSRHV())
                        {
                            best = cand;
                        }
                        else if (cand.getSRHV() == best.getSRHV())
                        {
                            if ((rnd.Next(0, 100) % 2) == 0)
                            {
                                best = cand;
                            }
                        }
                    }
                }
                if (depth == 0)
                {
                    next = best;
                }
                else
                {
                    next = root.DeepClone(root);
                    next.copyHValues(best);
                }
            }

            
            return next;
        }

        

        static AIMove findMin(AIMove root, int depth)
        {
            AIMove next = null;

            if (depth == dlimit)
            {
                return root;
            }
            else
            {
                AIMove best = null;
                ArrayList moves = new ArrayList();
                moves = generateBlackMoves(root);

                for (int i = 0; i < moves.Count; i++)
                {
                    AIMove cand = findMax((AIMove)moves[i], depth + 1);
                    
                    if (cand == null)
                    {
                    }
                    else if (best == null)
                    {
                        best = cand;
                    }
                    else if (cand.getPHV() < best.getPHV())
                    {
                        best = cand;
                    }
                    else if (cand.getPHV() == best.getPHV())
                    {
                        if (cand.getSBHV() > best.getSBHV())
                        {
                            best = cand;
                        }
                        else if (cand.getSBHV() == best.getSBHV())
                        {
                            if ((rnd.Next(0,100) % 2) == 0)
                            {
                                best = cand;
                            }
                        }
                    }
                }
                if (depth == 0)
                {
                    next = best;
                }
                else
                {
                    next = root.DeepClone(root);
                    next.copyHValues(best);
                }
            }

            return next;
        }

        //Check Range Method
        internal static Boolean checkRange(int i, int j)
        {
            Boolean movable = false;
            if (i < 8 && i >= 0 && j < 8 && j >= 0)
            {
                movable = true;
            }
            return movable;
        }

        //Check for Possible Checker Piece Jump AIMove
        internal static AIMove checkJump(AIMove root, int type, int i, int j, int o, int p, int s, int t)
        {
            int[,] board = root.getboard();
            AIMove jump = null;
            if (checkRange(s, t))
            {
                if (board[s,t] == 0)
                {
                    jump = new AIMove(root.DeepClone(root));
                    jump.edit(i, j, 0);
                    jump.edit(o, p, 0);
                    jump.edit(s, t, type);
                    
                    //Implement Double Jump Check
                    //...

                    //Jump - King Me
                    if (s == 0 && type == 1)
                    {
                        jump.edit(s, t, 3);
                    }
                    else if (s == 7 && type == 2)
                    {
                        jump.edit(s, t, 4);
                    }
                }
            }
            return jump;
        }

        internal static ArrayList generateRedMoves(AIMove root)
        {
            ArrayList movelist = new ArrayList();
            AIMove rootC = root.DeepClone(root);

            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    int[,] board = (rootC.DeepClone(rootC)).getboard();
                    if (board[i, j] == 1)
                    {
                        if(checkRange(i-1,j-1))
                        {
                            if (board[i - 1, j - 1] == 0)
                            {
                                
                                AIMove slide = new AIMove(root.DeepClone(root));
                                slide.edit(i, j, 0);
                                slide.edit(i - 1, j - 1, 1);

                                //King Me
                                if (i == 0)
                                {
                                    slide.edit(i - 1, j - 1, 3);
                                }
                                slide.evaluateBoard();
                                movelist.Add(slide);
                            }
                            else if (board[i - 1, j - 1] == 2 || board[i - 1, j - 1] == 4)
                            {
                                AIMove jump = checkJump(root.DeepClone(root), 1, i, j, i - 1, j - 1, i - 2, j - 2);
                                if (jump != null)
                                {
                                    jump.evaluateBoard();
                                    movelist.Add(jump);
                                }
                            }
                        }
                        if (checkRange(i - 1, j + 1))
                        {
                            if (board[i - 1, j + 1] == 0)
                            {
                                AIMove slide = new AIMove(root.DeepClone(root));
                                slide.edit(i, j, 0);
                                slide.edit(i - 1, j + 1, 1);
                                
                                //King Me
                                if (i == 0)
                                {
                                    slide.edit(i - 1, j + 1, 3);
                                }
                                slide.evaluateBoard();
                                movelist.Add(slide);
                            }
                            else if (board[i - 1, j + 1] == 2 || board[i - 1, j + 1] == 4)
                            {
                                AIMove jump = checkJump(root.DeepClone(root), 1, i, j, i - 1, j + 1, i - 2, j + 2);
                                if (jump != null)
                                {
                                    jump.evaluateBoard();
                                    movelist.Add(jump);
                                }
                            }
                        }
                    }
                    else if (board[i, j] == 3)
                    {

                    }
                }
            }

            return movelist;
        }

        internal static ArrayList generateBlackMoves(AIMove root)
        {
            ArrayList movelist = new ArrayList();
            AIMove rootC = root.DeepClone(root);

            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {

                    int[,] board = rootC.getboard();

                    if (board[i, j] == 2)
                    {
                        if (checkRange(i + 1, j - 1))
                        {
                            if (board[i + 1, j - 1] == 0)
                            {
                                AIMove slide = new AIMove(root.DeepClone(root));
                                slide.edit(i, j, 0);
                                slide.edit(i + 1, j - 1, 2);

                                //King Me
                                if (i == 7)
                                {
                                    slide.edit(i + 1, j - 1, 4);
                                }
                                slide.evaluateBoard();
                                movelist.Add(slide);
                            }
                            else if (board[i + 1, j - 1] == 1 || board[i + 1, j - 1] == 3)
                            {
                                AIMove jump = checkJump(root.DeepClone(root), 2, i, j, i + 1, j - 1, i + 2, j - 2);
                                if (jump != null)
                                {
                                    jump.evaluateBoard();
                                    movelist.Add(jump);
                                }
                            }
                        }
                        if (checkRange(i + 1, j + 1))
                        {
                            if (board[i + 1, j + 1] == 0)
                            {
                                AIMove slide = new AIMove(root.DeepClone(root));
                                slide.edit(i, j, 0);
                                slide.edit(i + 1, j + 1, 2);
                                //King Me
                                if (i == 7)
                                {
                                    slide.edit(i + 1, j + 1, 4);
                                }
                                slide.evaluateBoard();
                                movelist.Add(slide);
                            }
                            else if (board[i + 1, j + 1] == 1 || board[i + 1, j + 1] == 3)
                            {
                                AIMove jump = checkJump(root.DeepClone(root), 2, i, j, i + 1, j + 1, i + 2, j + 2);
                                if (jump != null)
                                {
                                    jump.evaluateBoard();
                                    movelist.Add(jump);
                                }
                            }
                        }
                    }
                    else if (board[i, j] == 4)
                    {

                    }
                }
            }

            return movelist;
        }
    }


    [Serializable]
    class AIMove
    {
        private int[,] rootboard;
        private AIMove nextState;
        private AIMove prevState;
        private int phv;
        private int srhv;
        private int sbhv;
        private int[,] boardvalues = {
											{0, 4, 0, 4, 0, 4, 0, 4},
											{4, 0, 3, 0, 3, 0, 3, 0},
											{0, 3, 0, 2, 0, 2, 0, 4},
											{4, 0, 2, 0, 1, 0, 3, 0},
											{0, 3, 0, 1, 0, 2, 0, 4},
											{4, 0, 2, 0, 2, 0, 3, 0},
											{0, 2, 0, 3, 0, 3, 0, 4},
											{4, 0, 4, 0, 4, 0, 4, 0}
											};

        public AIMove(int[,] board)
        {
            this.rootboard = new int[8, 8];
            this.nextState = null;
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    rootboard[i, j] = board[i, j];
                }
            }

            this.phv = 0;
            this.srhv = 0;
            this.sbhv = 0;
        }
        public AIMove(AIMove root)
        {
            this.rootboard = (root.DeepClone(root)).getboard();
            this.nextState = null;
            this.prevState = root;

            this.phv = 0;
            this.srhv = 0;
            this.sbhv = 0;
            /*int[,] board = root.getboard();
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    rootboard[i, j] = board[i, j];
                }
            }*/
        }

        public void evaluateBoard()
        {
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {

                    if (rootboard[i, j] == 0)
                    { //Empty Cell
                    }
                    else if (rootboard[i, j] == 1)
                    { //Red Regular
                        this.phv += 1;
                        this.srhv += boardvalues[i, j];
                    }
                    else if (rootboard[i, j] == 2)
                    { //Black Regular
                        this.phv -= 1;
                        this.sbhv += boardvalues[i, j];
                    }
                    else if (rootboard[i, j] == 3)
                    { //Red King
                        this.phv += 2;
                        this.srhv += boardvalues[i, j];
                    }
                    else if (rootboard[i, j] == 4)
                    { //Black King
                        this.phv -= 2;
                        this.sbhv += boardvalues[i, j];
                    }
                }
            }
        }

        public int[,] getboard()
        {
            return rootboard;
        }

        public void edit(int i, int j, int value)
        {
            this.rootboard[i, j] = value;
        }

        public int getPHV()
        {
            return phv;
        }

        public int getSRHV()
        {
            return srhv;
        }
        public int getSBHV()
        {
            return sbhv;
        }

        public void setPrevState(AIMove prev)
        {
            this.prevState = prev;
        }
        public AIMove getPrevState()
        {
            return prevState;
        }

        public void setNextState(AIMove next)
        {
            this.nextState = next;
        }
        public AIMove getNextState()
        {
            return nextState;
        }

        public void printboard()
        {

            Console.WriteLine();

            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    Console.Write(" " + rootboard[i, j] + " ");
                }
                Console.WriteLine();
            }
        }

        public void copyHValues(AIMove source)
        {
            this.phv = source.getPHV();
            this.sbhv = source.getSBHV();
            this.srhv = source.getSRHV();
        }

        public AIMove DeepClone<AIMove>(AIMove obj)
        {
            using(var ms = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(ms, obj);
                ms.Position = 0;

                return (AIMove)formatter.Deserialize(ms);
            }

        }
    }
}
